package com.mlh.dp.背包问题.完全背包;

import org.junit.Test;

import java.util.Arrays;

/**
 * @author 缪林辉
 * @date 2024/4/20 11:27
 * @DESCRIPTION
 */
// 给你一个整数数组 coins ，表示不同面额的硬币；以及一个整数 amount ，表示总金额。
// 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额，返回 -1 。
// 你可以认为每种硬币的数量是无限的。
// 输入：coins = [1, 2, 5], amount = 11
// 输出：3
// 解释：11 = 5 + 5 + 1
public class 零钱兑换 {
    // 确认是完全背包问题
    // 凑成总金额所需的最少的硬币个数，因此需要比较，取较小值
    public int method1(int[] coins, int amount) {
        int[] dp = new int[amount + 1];// 值是凑成下标i所需要的最少硬币数
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                if (dp[j - coins[i]] != Integer.MAX_VALUE) {
                    dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
                }

            }
        }
        return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
    }

    // 方法2把MAX_VALUE换成MAX_VALUE/2，这样可以少一个判断，因此执行时间会更快，
    // 但是仅仅因为leetcode的测试用例不够全面导致可以通过
    // 猜想：为什么没有这种案例，这样的案例非常难找出来？即最少硬币数要大于MAX_VALUE/2
    public int method2(int[] coins, int amount) {
        int[] dp = new int[amount + 1];// 值是凑成下标i所需要的最少硬币数
        Arrays.fill(dp, Integer.MAX_VALUE / 2);
        dp[0] = 0;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
            }
        }
        return dp[amount] == Integer.MAX_VALUE / 2 ? -1 : dp[amount];
    }

    public int practice(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, Integer.MAX_VALUE / 2);
        dp[0] = 0;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                dp[j] = Math.min(dp[j - coins[i]] + 1, dp[j]);
            }
        }
        return dp[amount] >= Integer.MAX_VALUE / 2 ? -1 : dp[amount];
    }

    public int practice2(int[] coins, int amount) {
        int[]dp=new int[amount+1];
        Arrays.fill(dp,Integer.MAX_VALUE/2);
        dp[0]=0;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <=amount ; j++) {
                dp[j]=Math.min(dp[j-coins[i]]+1,dp[j]);
            }
        }
        return dp[amount]>=Integer.MAX_VALUE/2?-1:dp[amount];
    }

}
